﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using CreditAgricole.Framework;
using System.Collections;
using CreditAgricole.GenerateData.Tools;

namespace CreditAgricole.GenerateData
{
    class Program
    {
        const int SLEEP_TIME = 1000; // milliseconds to wait with Thread.Sleep(). For better performances, don't set a value lesser than 500 !

        static Random rnd = new Random(); // Initialize a unique random object for all generated numbers

        static string siteUrl = string.Empty; // absolute site url
        static string webUrl = string.Empty; // relative web url
        static int qty = 0; // quantity of items to create
        static string datatype = string.Empty; // data type (PDE: Production Deployment, TPR: Technical Procedure, KDB: Knowledge Database)

        static byte[] fileContent = System.Text.Encoding.Default.GetBytes("This is an auto-generated file."); // text file content
        static SPUserCollection approvers1 = null;
        static SPUserCollection approvers2 = null;

        static void Main(string[] args)
        {
            /* Arguments :
             * -Site:<AbsoluteUrl>
             * -Web:<RelativeUrl>
             * -Num:<Number of items to generate>
             * -Type:<data type> (PDE for Production Deployment, KDB for Knowledge Database, TPR for Technical Procedures)
             */

            Console.Title = "Crédit Agricole - Génération aléatoire de données...";
            Console.SetWindowSize(100, 25);
            Console.SetBufferSize(100, 25);
            Console.CursorVisible = false;

            if (CheckArgs(args))
            {                                
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Site : {0}\nWeb : {1}\nNombre d'éléments à créer : {2}", siteUrl, webUrl, qty);
                Console.ResetColor();

                switch (datatype)
                {
                    case "PDE":
                        GenerateMEPDatas();
                        break;
                    case "KDB":
                        GenerateKnowledgeDatas();
                        break;
                    case "TPR":
                    default:
                        Console.WriteLine("Procédure '{0}' pas encore implémentée.", datatype);
                        break;
                }
            }
            
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine("<Press any key to exit>");
            Console.ResetColor();
            Console.ReadKey(true);
            return;
        }

        static bool CheckArgs(string[] args)
        {
            List<string> parameters = new List<string>();
            bool bSiteOK = false, bNumOK = false, bTypeOK = false, bWebOK = false;

            // Each arg must start with '-'
            // if not, it will be appended to the previous one
            string arg = string.Empty;

            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].StartsWith("-"))
                {
                    // Save previous arg, if not empty
                    if (arg != string.Empty)
                    {
                        parameters.Add(arg);
                    }

                    arg = args[i];
                }
                else
                {
                    arg += string.Format(" {0}", args[i]); // Inject space
                }
            }

            // Save last param if not empty
            if (arg != string.Empty) parameters.Add(arg);

            foreach (string s in parameters)
            {
                string tmp = s.ToLower();

                if (tmp.StartsWith("-site:"))
                {
                    siteUrl = s.Remove(0, 6);
                    Uri tmpUri;
                    if (!Uri.TryCreate(siteUrl, UriKind.Absolute, out tmpUri))
                    {
                        Console.WriteLine("{0}\n{1} n'est pas une url valide.", s, siteUrl);
                        return false;
                    }
                    bSiteOK = true;
                }
                else if (tmp.StartsWith("-web:"))
                {
                    webUrl = s.Remove(0, 5);
                    Uri tmpUri;
                    if (!Uri.TryCreate(webUrl, UriKind.Relative, out tmpUri))
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("{0}\n{1} n'est pas une url valide.", s, webUrl);
                        Console.ResetColor();
                        return false;
                    }
                    bWebOK = true;
                }
                else if (tmp.StartsWith("-num:"))
                {
                    string val = s.Remove(0, 5);
                    if (int.TryParse(val, out qty))
                    {
                        if (qty < 1)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("{0}\nLa valeur doit être supérieure à 0.", s);
                            Console.ResetColor();
                            return false;
                        }
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("{0}\n{1} n'est pas une valeur entière correcte.", s, val);
                        Console.ResetColor();
                        return false;
                    }
                    bNumOK = true;
                }
                else if (tmp.StartsWith("-type:"))
                {
                    datatype = s.Remove(0, 6).ToUpper();

                    switch (datatype)
                    {
                        case "PDE":
                        case "KDB":
                        case "TPR":
                            break;                            
                        default:
                            Console.WriteLine("{0}\n{1} n'est pas une type valide.\nLes types valides sont :\n\n\tPDE (Production Deployment)\n\tKDB (Knowledge Database)\n\tTPR (Technical Procedure)", s, datatype);
                            return false;
                    }
                    bTypeOK = true;
                }
                else
                {
                    // Invalid Argument 
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("{0} n'est pas un argument correct.", s);
                    Console.ResetColor();
                    return false;
                }
            }

            // Check if all params present
            if (bSiteOK && bWebOK && bNumOK && bTypeOK)
            {
                return true;
            }
            else
            {
                Console.WriteLine("Arguments incorrects.\nSyntaxe :\nGenerateData -Site:<url absolue site> -Web:<url relative web> -Num:<nombre d'éléments à créer> -Type:[PDE|KDB|TPR]");
                return false;
            }
        }

        static string GenerateRandomName()
        {
            const string CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789-_";

            string name = string.Empty;
            int length = 0;

            length = rnd.Next(6, 13); // string length between 6 and 12

            for (int i = 0; i < length; i++)
            {
                // Generate a random character from CHARACTERS constant
                name += CHARACTERS.Substring(rnd.Next(CHARACTERS.Length), 1);
            }

            return name;
        }

        static string GenerateRandomExtension()
        {
            string[] exts = { "txt", "rtf", "doc", "ini", "log" };
            return exts[rnd.Next(exts.Length)];
        }

        static string GenerateRandomString()
        {
            const string CHARACTERS = "abcdefghijklmnopqrstuvwxyz   ";

            string str = string.Empty;
            int length = 0;

            length = rnd.Next(20, 61); // string length between 20 and 60

            for (int i = 0; i < length; i++)
            {
                // Generate a random character from CHARACTERS constant
                str += CHARACTERS.Substring(rnd.Next(CHARACTERS.Length), 1);
            }

            return str.Trim();
        }

        static SPUser GetRandomUser(ApproverLevel level)
        {
            SPUserCollection users = null;

            if (level == ApproverLevel.Level1)
                users = approvers1;
            else
                users = approvers2;

            if (users != null && users.Count > 0)
                return users[rnd.Next(users.Count)];
            else
                return null;
        }

        static string CurrentSequenceChar = "*"; // Char not in the sequence for initialization
        static void AnimateCursor()
        {
            string sequence = @"-\|/";
            int top = 0, left = 0;
            string charToWrite = "";

            // Get current position
            top = Console.CursorTop;
            left = Console.CursorLeft;

            // Write next char in sequence
            int index = sequence.IndexOf(CurrentSequenceChar);
            if (index == sequence.Length - 1)
                charToWrite = sequence.Substring(0, 1);
            else
                charToWrite = sequence.Substring(index + 1, 1);

            CurrentSequenceChar = charToWrite;
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.Write(charToWrite);
            Console.ResetColor();

            // Reposition for overwriting
            Console.SetCursorPosition(left, top);
        }

        public static void CompleteTask(SPWorkflowTask task, bool isApproved)
        {
            Hashtable ht = new Hashtable(); 
            ht[SPBuiltInFieldId.Completed] = "TRUE"; 
            ht[SPBuiltInFieldId.PercentComplete] = 1.0F; // 100%
            ht[SPBuiltInFieldId.TaskStatus] = SPResource.GetString(new CultureInfo((int)task.Web.Language, false), Strings.WorkflowStatusCompleted, new object[0]);
            ht["Status"] = "Completed";
            if (isApproved)
            {
                ht[SPBuiltInFieldId.WorkflowOutcome] = "Approved"; 
                ht["TaskStatus"] = "Approved";
            }
            else
            {
                ht[SPBuiltInFieldId.WorkflowOutcome] = "Rejected";
                ht["TaskStatus"] = "Rejected";
            }
            
            ht["FormData"] = SPWorkflowStatus.Completed; 

            // alter the task using a trick to prevent task lock issue   
            if (!AlterTask(task, ht, true, 10))
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("\nLa tâche n'a pas pu être traitée.");
                Console.ResetColor();
            }
        }

        public static bool AlterTask(SPListItem task, Hashtable htData, bool fSynchronous, int attempts)
        {
            if ((int)task[SPBuiltInFieldId.WorkflowVersion] != 1)
            {
                SPList parentList = task.ParentList.ParentWeb.Lists[new Guid(task[SPBuiltInFieldId.WorkflowListId].ToString())];
                SPListItem parentItem = parentList.Items.GetItemById((int)task[SPBuiltInFieldId.WorkflowItemId]);
                for (int i = 0; i < attempts; i++)
                {
                    SPWorkflow workflow = parentItem.Workflows[new Guid(task[SPBuiltInFieldId.WorkflowInstanceID].ToString())];
                    if (!workflow.IsLocked)
                    {
                        task[SPBuiltInFieldId.WorkflowVersion] = 1;
                        task.SystemUpdate();
                        break;
                    }
                    if (i != attempts - 1)
                    {
                        Thread.Sleep(SLEEP_TIME);
                        AnimateCursor();
                    }
                }
            }
            return SPWorkflowTask.AlterTask(task, htData, fSynchronous);
        }

        public static void GenerateMEPDatas()
        {
            Console.Title = "Crédit Agricole - Génération aléatoire de données de mise en production";

            try
            {
                // Open Sharepoint web
                using (SPSite site = new SPSite(siteUrl))
                {
                    using (SPWeb web = site.OpenWeb(webUrl))
                    {
                        Console.WriteLine("Web ouvert.");

                        // Check if document library exists
                        try
                        {
                            SPDocumentLibrary doclib = web.Lists[Constants.ChangingListTitle] as SPDocumentLibrary;

                            if (doclib != null)
                            {
                                Console.WriteLine("Liste '{0}' trouvée...", doclib.Title);
                                Console.Write("Récupération des approbateurs de niveau 1... ");
                                try
                                {
                                    approvers1 = web.SiteGroups[Constants.MEPApproversLvl1GroupName].Users;
                                    Console.ForegroundColor = ConsoleColor.Green;
                                    Console.WriteLine("Ok.");
                                }
                                catch
                                {
                                    Console.ForegroundColor = ConsoleColor.Red;
                                    Console.WriteLine("Erreur.");
                                }
                                Console.ResetColor();
                                Console.Write("Récupération des approbateurs de niveau 2... ");
                                try
                                {
                                    approvers2 = web.SiteGroups[Constants.MEPApproversLvl2GroupName].Users;
                                    Console.ForegroundColor = ConsoleColor.Green;
                                    Console.WriteLine("Ok.");
                                }
                                catch
                                {
                                    Console.ForegroundColor = ConsoleColor.Red;
                                    Console.WriteLine("Erreur.");
                                }
                                Console.ResetColor();
                                Console.Write("Création de ");
                                Console.ForegroundColor = ConsoleColor.White;
                                Console.Write("{0}", qty);
                                Console.ResetColor();
                                Console.WriteLine(" élément{0}.", qty > 1 ? "s" : "");

                                // Memorise cursor position for filename;
                                CursorPosition filenamePosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);
                                CursorPosition messagePosition = new CursorPosition(Console.CursorTop + 1, Console.CursorLeft);
                                CursorPosition currentPosition;

                                Console.WriteLine(); // filename
                                Console.WriteLine(); // message

                                // Loop to create documents
                                for (int i = 0; i < qty; i++)
                                {
                                    try
                                    {
                                        // Generate a random filename                                            
                                        string filename = string.Format("{0}.{1}", GenerateRandomName(), GenerateRandomExtension());

                                        // Memorise current cursor position
                                        currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                        // Write filename
                                        Console.SetCursorPosition(filenamePosition.Left, filenamePosition.Top);
                                        Console.Write("Fichier en cours : ");
                                        Console.ForegroundColor = ConsoleColor.Cyan;
                                        Console.WriteLine("{0}", (filename + "                    ").Substring(0, 20));
                                        Console.WriteLine("Génération des métadonnées                    ");
                                        Console.ResetColor();

                                        // Place cursor at previous position
                                        Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                        // Add a new item
                                        SPUser user = GetRandomUser(ApproverLevel.Level1);
                                        SPFile f = doclib.RootFolder.Files.Add(doclib.RootFolder.Url + "/" + filename, fileContent, null, user, user, DateTime.Now, DateTime.Now, false);
                                        f.Update();

                                        // Wait for Added event receiver to execute (status = Pas en Production)
                                        SPListItem tmpItem = doclib.GetItemById(f.Item.ID);
                                        while (tmpItem == null || tmpItem["CAMEPStatus"] == null)
                                        {
                                            // Cannot work directly on f.Item because it's not refreshed on access
                                            System.Threading.Thread.Sleep(SLEEP_TIME);
                                            AnimateCursor();
                                            tmpItem = doclib.GetItemById(f.Item.ID);
                                        }

                                        // Update item
                                        tmpItem["CATitle"] = filename;
                                        tmpItem["CADueDate"] = DateTime.Today.AddDays(i % 30);
                                        tmpItem["CAImpactRisk"] = GenerateRandomString();
                                        tmpItem["CADescription"] = GenerateRandomString();
                                        tmpItem["CADomain"] = (rnd.Next(3) + 1).ToString();
                                        tmpItem["CAEntity"] = (rnd.Next(2) + 1).ToString();
                                        SPUser user1 = GetRandomUser(ApproverLevel.Level1);
                                        SPUser user2 = GetRandomUser(ApproverLevel.Level2);
                                        tmpItem["CAApproverLvl1"] = user1;
                                        tmpItem["CAApproverLvl2"] = user1.Name == user2.Name ? null : user2; // Reset Lvl2 if same user in both fields

                                        // Let the status as is when i is divisible by 16 because item will be rejected in this case
                                        // If divisible by 2, set status to "En Production", except if divisible by 8. Then set status to "Clôturé"
                                        if (i % 2 == 0 && i % 16 != 0)
                                        {
                                            if (i % 8 == 0)
                                                tmpItem["CAMEPStatus"] = "Clôturé";
                                            else if (i % 8 > 4)
                                                tmpItem["CAMEPStatus"] = "En Production";
                                        }

                                        tmpItem.SystemUpdate(); // Don't scratch modified informations

                                        // Wait until SharePoint has created the workflow approval task
                                        // Memorise current cursor position
                                        currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                        // Write message
                                        Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                        Console.ForegroundColor = ConsoleColor.Cyan;
                                        Console.WriteLine("Attente création tâche niveau 1...            ");
                                        Console.ResetColor();

                                        // Place cursor at previous position
                                        Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                        while (tmpItem.Tasks.Count == 0)
                                        {
                                            System.Threading.Thread.Sleep(SLEEP_TIME);
                                            AnimateCursor();
                                        }

                                        // Approve or reject document each time i is even
                                        if (i % 2 == 0)
                                        {
                                            if (i % 16 == 0)
                                            {
                                                // Reject
                                                // Memorise current cursor position
                                                currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                // Write message
                                                Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                Console.ForegroundColor = ConsoleColor.Cyan;
                                                Console.WriteLine("Rejet par niveau 1...                         ");
                                                Console.ResetColor();

                                                // Place cursor at previous position
                                                Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                                CompleteTask(tmpItem.Tasks[0], false);
                                            }
                                            else
                                            {
                                                // Approve
                                                // Memorise current cursor position
                                                currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                // Write message
                                                Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                Console.ForegroundColor = ConsoleColor.Cyan;
                                                Console.WriteLine("Approbation par niveau 1...                   ");
                                                Console.ResetColor();

                                                // Place cursor at previous position
                                                Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                                CompleteTask(tmpItem.Tasks[0], true);

                                                // if Approver2 is not null, approve second task
                                                if (tmpItem["CAApproverLvl2"] != null)
                                                {
                                                    // Wait until SharePoint has created the second workflow approval task
                                                    // Memorise current cursor position
                                                    currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                    // Write message
                                                    Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                    Console.ForegroundColor = ConsoleColor.Cyan;
                                                    Console.WriteLine("Attente création tâche niveau 2...            ");
                                                    Console.ResetColor();

                                                    // Place cursor at previous position
                                                    Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);
                                                    while (tmpItem.Tasks.Count == 1)
                                                    {
                                                        System.Threading.Thread.Sleep(SLEEP_TIME);
                                                        AnimateCursor();
                                                    }

                                                    // Approve second task (tasks collection sorted descending ! second task is at index 0 !)
                                                    // Memorise current cursor position
                                                    currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                    // Write message
                                                    Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                    Console.ForegroundColor = ConsoleColor.Cyan;
                                                    Console.WriteLine("Approbation par niveau 2...                   ");
                                                    Console.ResetColor();

                                                    // Place cursor at previous position
                                                    Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);
                                                    CompleteTask(tmpItem.Tasks[0], true);
                                                }

                                            }
                                        }

                                        // Show progression
                                        Console.Write(".");
                                    }
                                    catch (Exception ex)
                                    {
                                        Console.ForegroundColor = ConsoleColor.Red;
                                        Console.WriteLine("\nERREUR : {0}", ex.Message);
                                        Console.ResetColor();
                                    }
                                }

                                // Memorise current cursor position
                                currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                // Clear last filename
                                Console.SetCursorPosition(filenamePosition.Left, filenamePosition.Top);
                                Console.Write("Fichier en cours : ");
                                Console.WriteLine("                     ");
                                Console.WriteLine("                                              ");

                                // Place cursor at previous position
                                Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                // Fin de traitement
                                Console.WriteLine("\nTraitement terminé.");
                            }
                            else
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("La liste '{0}' n'existe pas.");
                                Console.ResetColor();
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("Une erreur s'est produite :\n\n{0}", ex.Message);
                            Console.ResetColor();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Une erreur s'est produite :\n\n{0}", ex.Message);
                Console.ResetColor();
            }
        }

        public static void GenerateKnowledgeDatas()
        {
            Console.Title = "Crédit Agricole - Génération aléatoire de données de base de connaissances";

            try
            {
                // Open Sharepoint web
                using (SPSite site = new SPSite(siteUrl))
                {
                    using (SPWeb web = site.OpenWeb(webUrl))
                    {
                        Console.WriteLine("Web ouvert.");

                        // Check if document library exists
                        try
                        {
                            SPDocumentLibrary doclib = web.Lists[Constants.KnowledgeUnapprovedListTitle] as SPDocumentLibrary;

                            if (doclib != null)
                            {
                                Console.WriteLine("Liste '{0}' trouvée...", doclib.Title);
                                Console.Write("Récupération des approbateurs de niveau 1... ");
                                try
                                {
                                    approvers1 = web.SiteGroups[Constants.KnowledgeApproversLvl1GroupName].Users;
                                    Console.ForegroundColor = ConsoleColor.Green;
                                    Console.WriteLine("Ok.");
                                }
                                catch
                                {
                                    Console.ForegroundColor = ConsoleColor.Red;
                                    Console.WriteLine("Erreur.");
                                }
                                Console.ResetColor();
                                Console.Write("Récupération des approbateurs de niveau 2... ");
                                try
                                {
                                    approvers2 = web.SiteGroups[Constants.KnowledgeApproversLvl2GroupName].Users;
                                    Console.ForegroundColor = ConsoleColor.Green;
                                    Console.WriteLine("Ok.");
                                }
                                catch
                                {
                                    Console.ForegroundColor = ConsoleColor.Red;
                                    Console.WriteLine("Erreur.");
                                }
                                Console.ResetColor();
                                Console.Write("Création de ");
                                Console.ForegroundColor = ConsoleColor.White;
                                Console.Write("{0}", qty);
                                Console.ResetColor();
                                Console.WriteLine(" élément{0}.", qty > 1 ? "s" : "");

                                // Memorise cursor position for filename;
                                CursorPosition filenamePosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);
                                CursorPosition messagePosition = new CursorPosition(Console.CursorTop + 1, Console.CursorLeft);
                                CursorPosition currentPosition;

                                Console.WriteLine(); // filename
                                Console.WriteLine(); // message

                                // Loop to create documents
                                for (int i = 0; i < qty; i++)
                                {
                                    try
                                    {
                                        // Generate a random filename                                            
                                        string filename = string.Format("{0}.{1}", GenerateRandomName(), GenerateRandomExtension());

                                        // Memorise current cursor position
                                        currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                        // Write filename
                                        Console.SetCursorPosition(filenamePosition.Left, filenamePosition.Top);
                                        Console.Write("Fichier en cours : ");
                                        Console.ForegroundColor = ConsoleColor.Cyan;
                                        Console.WriteLine("{0}", (filename + "                    ").Substring(0, 20));
                                        Console.WriteLine("Génération des métadonnées                    ");
                                        Console.ResetColor();

                                        // Place cursor at previous position
                                        Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                        // Add a new item
                                        SPUser user = GetRandomUser(ApproverLevel.Level1);
                                        SPFile f = doclib.RootFolder.Files.Add(doclib.RootFolder.Url + "/" + filename, fileContent, null, user, user, DateTime.Now, DateTime.Now, false);
                                        f.Update();

                                        // Update item
                                        SPListItem tmpItem = f.Item;
                                        tmpItem["Created"] = DateTime.Now;
                                        tmpItem["Modified"] = DateTime.Now;
                                        tmpItem["CATitle"] = filename;
                                        tmpItem["CAValidityDate"] = DateTime.Today.AddYears(1).AddDays(i % 30);
                                        tmpItem["CAComment"] = GenerateRandomString();
                                        tmpItem["CAProduct"] = (rnd.Next(3) + 1).ToString();
                                        tmpItem["CADocType"] = (rnd.Next(3) + 1).ToString();
                                        tmpItem["CAKeyword"] = (rnd.Next(2) + 1).ToString();
                                        SPUser user1 = GetRandomUser(ApproverLevel.Level1);
                                        SPUser user2 = GetRandomUser(ApproverLevel.Level2);
                                        tmpItem["CAApproverLvl1"] = user1;
                                        tmpItem["CAApproverLvl2"] = user1.Name == user2.Name ? null : user2; // Reset Lvl2 if same user in both fields

                                        // Start approval workflow if i divisible by 2
                                        if (i % 2 == 0)
                                        {
                                            tmpItem["CAResubmitWorkFlow"] = true;
                                        }

                                        tmpItem.SystemUpdate(); // Don't scratch modified informations

                                        // if workflow started
                                        if (i % 2 == 0)
                                        {
                                            // Wait until SharePoint has created the workflow approval task
                                            // Memorise current cursor position
                                            currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                            // Write message
                                            Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                            Console.ForegroundColor = ConsoleColor.Cyan;
                                            Console.WriteLine("Attente création tâche niveau 1...            ");
                                            Console.ResetColor();

                                            // Place cursor at previous position
                                            Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                            while (tmpItem.Tasks.Count == 0)
                                            {
                                                System.Threading.Thread.Sleep(SLEEP_TIME);
                                                AnimateCursor();
                                            }

                                            // Approve document, or reject it if i is divisible by 16
                                            if (i % 2 == 0)
                                            {
                                                if (i % 16 == 0)
                                                {
                                                    // Reject
                                                    // Memorise current cursor position
                                                    currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                    // Write message
                                                    Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                    Console.ForegroundColor = ConsoleColor.Cyan;
                                                    Console.WriteLine("Rejet par niveau 1...                         ");
                                                    Console.ResetColor();

                                                    // Place cursor at previous position
                                                    Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                                    CompleteTask(tmpItem.Tasks[0], false);
                                                }
                                                else
                                                {
                                                    // Approve
                                                    // Memorise current cursor position
                                                    currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                    // Write message
                                                    Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                    Console.ForegroundColor = ConsoleColor.Cyan;
                                                    Console.WriteLine("Approbation par niveau 1...                   ");
                                                    Console.ResetColor();

                                                    // Place cursor at previous position
                                                    Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                                    CompleteTask(tmpItem.Tasks[0], true);

                                                    // if Approver2 is not null, approve second task
                                                    if (tmpItem["CAApproverLvl2"] != null)
                                                    {
                                                        // Wait until SharePoint has created the second workflow approval task
                                                        // Memorise current cursor position
                                                        currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                        // Write message
                                                        Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                        Console.ForegroundColor = ConsoleColor.Cyan;
                                                        Console.WriteLine("Attente création tâche niveau 2...            ");
                                                        Console.ResetColor();

                                                        // Place cursor at previous position
                                                        Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);
                                                        while (tmpItem.Tasks.Count == 1)
                                                        {
                                                            System.Threading.Thread.Sleep(SLEEP_TIME);
                                                            AnimateCursor();
                                                        }

                                                        // Approve second task (tasks collection sorted descending ! second task is at index 0 !)
                                                        // Memorise current cursor position
                                                        currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                                        // Write message
                                                        Console.SetCursorPosition(messagePosition.Left, messagePosition.Top);
                                                        Console.ForegroundColor = ConsoleColor.Cyan;
                                                        Console.WriteLine("Approbation par niveau 2...                   ");
                                                        Console.ResetColor();

                                                        // Place cursor at previous position
                                                        Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);
                                                        CompleteTask(tmpItem.Tasks[0], true);
                                                    }

                                                }
                                            }
                                        }

                                        // Show progression
                                        Console.Write(".");
                                    }
                                    catch (Exception ex)
                                    {
                                        Console.ForegroundColor = ConsoleColor.Red;
                                        Console.WriteLine("\nERREUR : {0}", ex.Message);
                                        Console.ResetColor();
                                    }
                                }

                                // Memorise current cursor position
                                currentPosition = new CursorPosition(Console.CursorTop, Console.CursorLeft);

                                // Clear last filename
                                Console.SetCursorPosition(filenamePosition.Left, filenamePosition.Top);
                                Console.Write("Fichier en cours : ");
                                Console.WriteLine("                     ");
                                Console.WriteLine("                                              ");

                                // Place cursor at previous position
                                Console.SetCursorPosition(currentPosition.Left, currentPosition.Top);

                                // Fin de traitement
                                Console.WriteLine("\nTraitement terminé.");
                            }
                            else
                            {
                                Console.ForegroundColor = ConsoleColor.Red;
                                Console.WriteLine("La liste '{0}' n'existe pas.");
                                Console.ResetColor();
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("Une erreur s'est produite :\n\n{0}", ex.Message);
                            Console.ResetColor();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Une erreur s'est produite :\n\n{0}", ex.Message);
                Console.ResetColor();
            }
        }

        public static void DrawBox(int width, int height)
        {
            DrawBox(Console.CursorLeft, Console.CursorTop, width, height);
        }

        public static void DrawBox(int width, int height, ConsoleColor foregroundColor, ConsoleColor backgroundColor)
        {
            DrawBox(Console.CursorLeft, Console.CursorTop, width, height, foregroundColor, backgroundColor);
        }
        
        public static void DrawBox(int left, int top, int width, int height)
        {
            DrawBox(left, top, width, height, Console.ForegroundColor, Console.BackgroundColor);
        }

        public static void DrawBox(int left, int top, int width, int height, ConsoleColor foregroundColor, ConsoleColor backgroundColor)
        {
            if (left < 0 || top < 0 || width < 3 || height < 3 || top > Console.WindowHeight - 3 || left > Console.WindowWidth - 3 || width > Console.WindowWidth - left || height > Console.WindowHeight - top)
                throw new ArgumentOutOfRangeException();

            // Save current console colors
            ConsoleColor currentForeground = Console.ForegroundColor;
            ConsoleColor currentBackground = Console.BackgroundColor;

            // Set new colors
            Console.ForegroundColor = foregroundColor;
            Console.BackgroundColor = backgroundColor;

            // Draw box
            for (int i = 0; i < height; i++)
            {
                Console.SetCursorPosition(left, top + i);

                string first, last, inside;
                if (i == 0)
                {
                    first = "╔";
                    last = "╗";
                    inside = "═";
                }
                else if (i == height - 1)
                {
                    first = "╚";
                    last = "╝";
                    inside = "═";
                }
                else
                {
                    first = last = "║";
                    inside = " ";
                }

                Console.Write(first);
                for (int j = 1; j < width - 1; j++)
                {
                    Console.Write(inside);
                }
                Console.WriteLine(last);
            }

            // Reset previous colors
            Console.ForegroundColor = currentForeground;
            Console.BackgroundColor = currentBackground;
        }
    }

    enum ApproverLevel
    {
        Level1,
        Level2
    }

    public struct CursorPosition
    {
        int _top;
        int _left;

        public CursorPosition(int top, int left)
        {
            if (top < 0 || left < 0) throw new ArgumentOutOfRangeException("Argument must be positive or zero.");
            _top = top;
            _left = left;
        }

        public int Top
        {
            get { return _top; }
        }

        public int Left
        {
            get { return _left; }
        }
    }
}
